﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ProtoBuf.Meta;
using Upreader.Common;
using Upreader.Application.Model;
using ProtoBuf;
using System.Reflection;

namespace Upreader.Infrastructure.ProtoBuf
{
    static class ProtoBufMetaModelBuilder
    {
        private static TypeModel _cachedModel;

        static ProtoBufMetaModelBuilder()
        {
            RuntimeTypeModel model = TypeModel.Create();

            // Register modelBase with each subtypes.
            // Note that changes to ModelBase will have to be adepted here
            model.Add(typeof(ModelBase), false)
                 .Add(1, "Id")
                 .AddSubType(2, typeof(Server))
                 .AddSubType(3, typeof(Download))
                 .AddSubType(4, typeof(File))
                 .AddSubType(5, typeof(Segment));

            // And ofcourse register all subtypes
            AddTypeWithPropertiesToModel<Server>(model);
            AddTypeWithPropertiesToModel<Download>(model);
            AddTypeWithPropertiesToModel<File>(model);
            AddTypeWithPropertiesToModel<Segment>(model);

            model.Add(typeof(DataContext), false)
                 .Add(ReflectionUtility.GetPropertyName<DataContext>(x => x.Downloads))
                 .Add(ReflectionUtility.GetPropertyName<DataContext>(x => x.Files))
                 .Add(ReflectionUtility.GetPropertyName<DataContext>(x => x.Segments))
                 .Add(ReflectionUtility.GetPropertyName<DataContext>(x => x.Servers))
                 .Add(ReflectionUtility.GetPropertyName<DataContext>(x => x.NextDownloadId))
                 .Add(ReflectionUtility.GetPropertyName<DataContext>(x => x.NextFileId))
                 .Add(ReflectionUtility.GetPropertyName<DataContext>(x => x.NextSegmentId))
                 .Add(ReflectionUtility.GetPropertyName<DataContext>(x => x.NextServerId));

            _cachedModel = model.Compile();
        }

        private static void AddTypeWithPropertiesToModel<T>(RuntimeTypeModel model)
        {
            BindingFlags propertyFlags = BindingFlags.Instance | BindingFlags.Public;

            model.Add(typeof(T), false)
                 .Add(ReflectionUtility.GetPropertyNames<T>(propertyFlags).ToArray());
        }

        public static TypeModel Build()
        {
            return _cachedModel;
        }
    }
}
